package com.htfyun.eink.pw.draw.pointer;

import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.gson.annotations.SerializedName;
import com.htfyun.eink.pw.draw.IPWDraw;

import java.util.Objects;

public class PWPointer {

    /**
     * {@link MotionEvent#getEventTime()} 获取的是 {@link android.os.SystemClock#uptimeMillis} <br>
     * 我们 cache 的值 应该是 {@link System#currentTimeMillis()}
     */
    @SerializedName("tmC")
    public long eventTimeCache;

    /**
     * {@link MotionEvent#getEventTime()}
     */
    @SerializedName("tm")
    private long eventTime;

    /**
     * {@link MotionEvent#getToolType(int)}
     */
    @SerializedName("tp")
    public int toolType;

    /**
     * {@link MotionEvent#getAction()} & {@link MotionEvent#ACTION_MASK}
     */
    @SerializedName("a")
    public int action;

    /**
     * {@link MotionEvent#AXIS_X}
     */
    @SerializedName("x")
    public float x;
    /**
     * {@link MotionEvent#AXIS_Y}
     */
    @SerializedName("y")
    public float y;
    /**
     * {@link MotionEvent#AXIS_PRESSURE}
     */
    @SerializedName("p")
    public float pressure;
    /**
     * {@link MotionEvent#AXIS_TILT}
     */
    @SerializedName("tx")
    public float tiltX;
    /**
     * {@link MotionEvent#AXIS_ORIENTATION}
     */
    @SerializedName("ty")
    public float tiltY;

    /**
     * X方向加速度. 如果是 history 点, 此指为 null
     */
    @Nullable
    @SerializedName("xV")
    public Float xVelocity;
    /**
     * Y方向加速度. 如果是 history 点, 此指为 null
     */
    @Nullable
    @SerializedName("yV")
    public Float yVelocity;

    public PWPointer() {
    }

    public PWPointer(PWPointer other) {
        this.configBy(other);
    }

    public long getEventTime() {
        return eventTime;
    }

    public void configBy(PWPointer other) {
        if (other == null) {
            return;
        }
        this.eventTimeCache = other.eventTimeCache;
        this.eventTime = other.eventTime;
        this.toolType = other.toolType;
        this.action = other.action;
        this.x = other.x;
        this.y = other.y;
        this.pressure = other.pressure;
        this.tiltX = other.tiltX;
        this.tiltY = other.tiltY;
        this.xVelocity = other.xVelocity;
        this.yVelocity = other.yVelocity;
    }

    public void configBy(@NonNull MotionEvent event) {
        this.eventTimeCache = System.currentTimeMillis();
        this.eventTime = event.getEventTime();
        this.action = event.getAction();
        this.x = event.getX();
        this.y = event.getY();
        this.pressure = event.getPressure();
        this.tiltX = event.getAxisValue(MotionEvent.AXIS_TILT, 0);
        this.tiltY = event.getOrientation();
    }

    public void configBy(@NonNull MotionEvent.PointerCoords coords, long eventTime) {
        this.eventTimeCache = System.currentTimeMillis();
        this.eventTime = eventTime;
        this.action = MotionEvent.ACTION_MOVE;
        this.x = coords.x;
        this.y = coords.y;
        this.pressure = coords.pressure;
        this.tiltX = coords.getAxisValue(MotionEvent.AXIS_TILT);
        this.tiltY = coords.orientation;
    }

    @NonNull
    public PWPointerActionEnums toPWPointerActionEnums() {
        return PWPointerActionEnums.actionToPWPointerActionEnums(action);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PWPointer pwPointer = (PWPointer) o;
        return eventTimeCache == pwPointer.eventTimeCache;
    }

    @Override
    public int hashCode() {
        return Objects.hash(eventTimeCache);
    }

    @NonNull
    @Override
    public String toString() {
        return "PWPointer{" +
                "eventTimeCache=" + eventTimeCache +
                ", eventTime=" + eventTime +
                ", toolType=" + toolTypeToString(toolType) +
                ", action=" + MotionEvent.actionToString(action) +
                ", x=" + x +
                ", y=" + y +
                ", pressure=" + pressure +
                ", tiltX=" + tiltX +
                ", tiltY=" + tiltY +
                ", xVelocity=" + xVelocity +
                ", yVelocity=" + yVelocity +
                '}';
    }

    public String flattenToString() {
        StringBuilder sb = new StringBuilder(32);
        // WARNING: Do not change the format of this string, it must be
        // preserved because Rects are saved in this flattened format.
        sb.append(eventTimeCache);
        sb.append(',');
        sb.append(eventTime);
        sb.append(',');
        sb.append(toolType);
        sb.append(',');
        sb.append(action);
        sb.append(',');
        sb.append(x);
        sb.append(',');
        sb.append(y);
        sb.append(',');
        sb.append(pressure);
        sb.append(',');
        sb.append(tiltX);
        sb.append(',');
        sb.append(tiltY);
        sb.append(',');
        sb.append(xVelocity);
        sb.append(',');
        sb.append(yVelocity);
        sb.append('\n');
        return sb.toString();
    }

    public static void unflattenFromString(@Nullable String str, @Nullable PWPointer pointer) throws Exception {
        if (TextUtils.isEmpty(str)) {
            return;
        }
        if (pointer == null) {
            pointer = new PWPointer();
        }

        str = str.replaceAll("\n", "");

        String[] values = str.split(",");
        if (values.length != 11) {
            Log.e(IPWDraw.TAG, "PWPointer flatten(" + str + ") is not length 11");
            return;
        }

        try {
            pointer.eventTimeCache = Long.parseLong(values[0]);
            pointer.eventTime = Long.parseLong(values[1]);
            pointer.toolType = Integer.parseInt(values[2]);
            pointer.action = Integer.parseInt(values[3]);
            pointer.x = Float.parseFloat(values[4]);
            pointer.y = Float.parseFloat(values[5]);
            pointer.pressure = Float.parseFloat(values[6]);
            pointer.tiltX = Float.parseFloat(values[7]);
            pointer.tiltY = Float.parseFloat(values[8]);

            if ("null".equalsIgnoreCase(values[9])) {
                pointer.xVelocity = null;
            } else {
                pointer.xVelocity = Float.parseFloat(values[9]);
            }
            if ("null".equalsIgnoreCase(values[10])) {
                pointer.yVelocity = null;
            } else {
                pointer.yVelocity = Float.parseFloat(values[10]);
            }

        } catch (Exception e) {
            throw new Exception("unflattenFrom error, e = " + e.getMessage());
        }
    }

    ////////////////////////////

    public static String toolTypeToString(int toolType) {
        String symbolicName = TOOL_TYPE_SYMBOLIC_NAMES.get(toolType);
        return symbolicName != null ? symbolicName : Integer.toString(toolType);
    }

    private static final SparseArray<String> TOOL_TYPE_SYMBOLIC_NAMES = new SparseArray<>();

    static {
        SparseArray<String> names = TOOL_TYPE_SYMBOLIC_NAMES;
        names.append(MotionEvent.TOOL_TYPE_UNKNOWN, "TOOL_TYPE_UNKNOWN");
        names.append(MotionEvent.TOOL_TYPE_FINGER, "TOOL_TYPE_FINGER");
        names.append(MotionEvent.TOOL_TYPE_STYLUS, "TOOL_TYPE_STYLUS");
        names.append(MotionEvent.TOOL_TYPE_MOUSE, "TOOL_TYPE_MOUSE");
        names.append(MotionEvent.TOOL_TYPE_ERASER, "TOOL_TYPE_ERASER");
    }
}
